/*
* jQuery UI Tag-Selector!
* 
* Hulahoo.com 
*
* @version v1.0
* Dependencies:
*   jQuery v1.4+
*   jQuery UI v1.8+
*/
(function($) {

    $.widget('ui.tagselector', {
        options: {
            containerId: 'tagselector', 
            containerBodyId: 'selectorbody', 
            containerCloseId: 'selectorcloser', 
            enableCloseIcon: true, 
            itemClass: '.taglist', 

            // num per line 
            numPerRow: 3, 

            // Event callbacks.
            onTagSelected: null,
            beforeTagCreated: null, 
            beforeTagSelected: null,
            afterTagSelected: null, 
            beforeClose: null,
            afterClose: null, 

            // content and pre-selected tags 
            html: "",
            selectedTags: [],

            position: {},
            width: 400,
            height: "auto",
            imageTag: false,
            allowMultiple: true,
            // support two levels selector
            parentSelector: null,
            childSelector: null
        },


        _create: function() {
            // for handling static scoping inside callbacks
            this.container = this.getContainer(); 
            this.container.data("element", this.element);
            this.refresh(); 
            this._bindEvents();
            this._needReset = false; 
        },

        _init: function(){
            this.open();
        },
  
        refresh: function() {
            // reload data 
            if(this.container.find(this.options.itemClass).length > 0) {
               this.container.find(this.options.itemClass).parent().html(this.options.html);
            } else 
               this.container.find("#"+this.options.containerBodyId).first().html(this.options.html); 

            this._row = null; 

            if(this.options.imageTag)
               this.tagList = this.container.find(this.options.itemClass).find('img'); 
            else 
               this.tagList = this.container.find(this.options.itemClass).find('ul.tags, ol.tags').andSelf().last();

            // Add existing tags from the list, if any.
            // for better format, display 3 each row
            var self = this;
            var num = 0; 
            var width = this.options.width != 'auto' ? (parseInt(this.options.width)-(this.options.numPerRow*25))/this.options.numPerRow : 100;

            if(this.options.imageTag) {
               this.tagList.each(function() {
                  self.createImageTag($(this).closest(".imageitem"), width);
               });
            }
            else { 
               this.tagList.hide().children('li').each(function() {
                  if(num%self.options.numPerRow == 0) {
                     self.createRow(this); 
                  }
                  num++;
                  self.createTag(this, width);
                  $(this).remove();
               });
            }

            this._needReset = false; 

        },

         
        open: function() {
            if(this._needReset) {
               this.container = this.getContainer(); 
               this.container.data("element", this.element);
               this.refresh();
            }

            if(this.options.position && !this._isOpen) {
               this.container.show()
               .position(this.options.position )
               .hide()
               .fadeIn(); 
               this._isOpen = true;
            }
            else if(this.options.position) {
               this.container
               .position(this.options.position ); 
            }

        }, 

        close: function() {
            if($.isFunction(this.options.beforeClose) && !this.options.beforeClose(this, this.options))
               return;
               
            this.container.hide(); 
            this._isOpen = false;
            this._needReset = true; 
            this._row = null; 

            if($.isFunction(this.options.afterClose) && !this.options.afterClose(this, this.options))
               return;
        },

        destroy: function() {
            $.Widget.prototype.destroy.call( this );
            this.container.remove();
            return this; 
        },

        _closeByKey: function(e) {
            var keyCode = $.ui.keyCode;
            switch( e.keyCode ) {
               case keyCode.ESCAPE:
                  if(this._isOpen) {
                     this.close();
                     return false;
                  }
               break;
            }

            return true;
        },

        _bindEvents: function() {
            var self = this; 

            $(document).bind('mousedown.tagselector', function(e){
               if(self._isOpen && !$.contains(self.container.get(0), e.target) && !self.container.is(e.target)){
                  var shouldClose = true;
                  // if has parentSelector, check to see if the click is in parent 
                  if(self.options.parentSelector) {
                     var pcontainer = $("#"+self.options.parentSelector);
                     if(pcontainer.length >0 && ($.contains(pcontainer.get(0), e.target) || pcontainer.is(e.target))) {
                        shouldClose = false;
                     }
                  }

                  if(self.options.childSelector) {
                     var pcontainer = $("#"+self.options.childSelector);
                     if(pcontainer.length >0 && ($.contains(pcontainer.get(0), e.target) || pcontainer.is(e.target))) {
                        shouldClose = false;
                     }
                  }

                  if(shouldClose)
                     self.close();
               }
            }).bind('keydwn.tagselector', function(e) {
               return self._closeByKey(e);
            });

            this.element.bind('keydown.tagselector', function(e) {
               return self._closeByKey(e);
            });

            this.container.find("#"+this.options.containerCloseId).bind('click.tagselector', function(e) {
               if(self._isOpen) {
                  self.close();
               }
               return false;
            });
        },

        createRow: function(item, width) {
            if(!this._row)
               this._row = $('<div></div>').addClass('tagrow').insertBefore(this.tagList);
            else 
               this._row = $('<div></div>').addClass('tagrow').insertAfter(this._row);
        }, 

        createTag: function(item, width) {
            var self = this;

            var value = $(item).html(),
                additionalClass = $(item).attr('class'),
                text = $(item).attr('title'),
                hiddenValue = $(item).attr('value'),
                extraValue = $(item).attr('xvalue'),

            // Automatically trims the value of leading and trailing whitespace.
            value = $.trim(value);

            if (value === '') {
                return false;
            }

            // check out whether they are pre-selected 
            var checked = false;
            if($.isFunction(this.options.beforeTagCreated)) {
               checked = this.options.beforeTagCreated(item);
            }
            else if(this.options.selectedTags && this.options.selectedTags.length > 0) {
               checked = $.inArray(text, this.options.selectedTags) != -1;
            }

            var labelbox = $("<label></label>")
                 .addClass("ui-corner-all taglabel")
                 .addClass(checked ? "tagselected" : "")
                 .html(value);

            text ? labelbox.attr('title', text) : '';
            hiddenValue ? labelbox.attr('value', hiddenValue) : '';
            extraValue ? labelbox.attr('xvalue', extraValue) : '';

            // Create tag.
            var tag = $('<div></div>')
                .addClass('tagchoice')
                .addClass(additionalClass)
                .addClass(checked ? "ui-state-active" : "")
                .append(labelbox)
                .css({width: width})
                .appendTo(this._row); 

            tag.delegate('label', 'click', function(e) {
               // if there is a childSelector, always call onTagSelected callback 
               if(!$(this).hasClass('tagselected') || self.options.childSelector) {
                  if($.isFunction(self.options.onTagSelected)) {
                     // update style based on function return 
                     if(self.options.onTagSelected(this, true, self.options))
                        $(this).addClass('tagselected').parent().addClass('ui-state-active');
                  }
                  else 
                     $(this).addClass('tagselected').parent().addClass('ui-state-active');
               } else if(!self.options.childSelector) {
                  if($.isFunction(self.options.onTagSelected)) {
                     if(self.options.onTagSelected(this, false, self.options))
                        $(this).removeClass('tagselected').parent().removeClass('ui-state-active');
                  }
                  else 
                     $(this).removeClass('tagselected').parent().removeClass('ui-state-active');
               }

               return false; 
            }).hover(function(){
               $(this).addClass('hover');
            }, function() {
               $(this).removeClass('hover');
            }); 
        },

        createImageTag: function(item, width) {
            var self = this;

            jQuery.metadata.setType("attr", "data");
            var data = $(item).metadata();

            if (!data) {
                return false;
            }

            // check out whether they are pre-selected 
            var checked = false;
            if($.isFunction(this.options.beforeTagCreated)) {
               checked = this.options.beforeTagCreated(item, this.options);
            }
            else if(this.options.selectedTags && this.options.selectedTags.length > 0) {
               checked = $.inArray(text, this.options.selectedTags) != -1;
            }

            if(checked) 
               $(item).find("img").addClass("tagselected ui-state-active");
            else 
               $(item).find("img").removeClass("tagselected ui-state-active");

            $(item).delegate("img", "click", function() {
               if(!$(this).hasClass('tagselected')) {
                  if($.isFunction(self.options.onTagSelected)) {
                     // update style based on function return 
                     if(self.options.onTagSelected(this, true, self.options))
                        $(this).addClass('tagselected').addClass('ui-state-active');
                  }
                  else 
                     $(this).addClass('tagselected').addClass('ui-state-active');
               } else if(!self.options.childSelector) {
                  // if there is a child selector, don't de-select 
                  if($.isFunction(self.options.onTagSelected)) {
                     if(self.options.onTagSelected(this, false, self.options))
                        $(this).removeClass('tagselected').removeClass('ui-state-active');
                  }
                  else 
                     $(this).removeClass('tagselected').removeClass('ui-state-active');
               }

               return false; 
            }).hover(function(){
               $(this).addClass('hover');
            }, function() {
               $(this).removeClass('hover');
            });
        },

        deselectAll: function() {
            if(this.options.imageTag)
               this.container.find(this.options.itemClass).find('img').removeClass("tagselected ui-state-active");
            else
               this.container.find(this.options.itemClass).find('label.tagselected').removeClass('tagselected').parent().removeClass('ui-state-active');
        },

        getContainer: function() {
            var existing = $("#" + this.options.containerId);
            if ( existing.size() > 0) {
               var container = existing.first().hide().css({width: this.options.width, height: this.options.height});
               container.find("#"+this.options.containerBodyId).first().empty();
               return container; 
            }
            else {
               var container =  $('<div id="' + this.options.containerId + '"></div>')
                      .appendTo('body')
                      .addClass('tagselector').css({width: this.options.width, height: this.options.height})
                      .zIndex( this.element.zIndex() + 1 )
                      .hide() 
                      .append('<div id="' + this.options.containerBodyId + '">');

               if(this.options.enableCloseIcon) {
                  container.append('<div id="' + this.options.containerCloseId + '">');
               }

               container.find("#"+this.options.containerBodyId);
               return container; 
            }
        }
    });

})(jQuery);


